#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define MODE_PERFORMANCE      1
#define MODE_PRECISION        2
#define MODE_BASE		         3

struct mi {
     int nr;
     char *name;
} months[] = {
	{ 1, "jan" }, { 2, "feb" }, { 3, "mar" }, { 4, "apr" },
	{ 5, "may" }, { 6, "jun" }, { 7, "jul" }, { 8, "aug" },
	{ 9, "sep" }, {10, "oct" }, {11, "nov" }, {12, "dec" }
};

struct angles_t {
	int angle;
	double sine;
	double step;
};

typedef struct angles_t angles_t;

angles_t angles[] = {
// {{{
{	0	,	0.0000000000	,	0.0087349294	},
{	1	,	0.0174524064	,	0.0087349294	},
{	2	,	0.0348994967	,	0.0087322687	},
{	3	,	0.0523359562	,	0.0087269480	},
{	4	,	0.0697564737	,	0.0087189690	},
{	5	,	0.0871557427	,	0.0087083341	},
{	6	,	0.1045284633	,	0.0086950466	},
{	7	,	0.1218693434	,	0.0086791105	},
{	8	,	0.1391731010	,	0.0086605307	},
{	9	,	0.1564344650	,	0.0086393127	},
{	10	,	0.1736481777	,	0.0086154632	},
{	11	,	0.1908089954	,	0.0085889893	},
{	12	,	0.2079116908	,	0.0085598991	},
{	13	,	0.2249510543	,	0.0085282014	},
{	14	,	0.2419218956	,	0.0084939060	},
{	15	,	0.2588190451	,	0.0084570233	},
{	16	,	0.2756373558	,	0.0084175645	},
{	17	,	0.2923717047	,	0.0083755416	},
{	18	,	0.3090169944	,	0.0083309675	},
{	19	,	0.3255681545	,	0.0082838556	},
{	20	,	0.3420201433	,	0.0082342204	},
{	21	,	0.3583679495	,	0.0081820770	},
{	22	,	0.3746065934	,	0.0081274413	},
{	23	,	0.3907311285	,	0.0080703298	},
{	24	,	0.4067366431	,	0.0080107601	},
{	25	,	0.4226182617	,	0.0079487501	},
{	26	,	0.4383711468	,	0.0078843190	},
{	27	,	0.4539904997	,	0.0078174862	},
{	28	,	0.4694715628	,	0.0077482721	},
{	29	,	0.4848096202	,	0.0076766978	},
{	30	,	0.5000000000	,	0.0076027851	},
{	31	,	0.5150380749	,	0.0075265565	},
{	32	,	0.5299192642	,	0.0074480353	},
{	33	,	0.5446390350	,	0.0073672453	},
{	34	,	0.5591929035	,	0.0072842112	},
{	35	,	0.5735764364	,	0.0071989582	},
{	36	,	0.5877852523	,	0.0071115124	},
{	37	,	0.6018150232	,	0.0070219003	},
{	38	,	0.6156614753	,	0.0069301493	},
{	39	,	0.6293203910	,	0.0068362873	},
{	40	,	0.6427876097	,	0.0067403429	},
{	41	,	0.6560590290	,	0.0066423454	},
{	42	,	0.6691306064	,	0.0065423245	},
{	43	,	0.6819983601	,	0.0064403107	},
{	44	,	0.6946583705	,	0.0063363352	},
{	45	,	0.7071067812	,	0.0062304296	},
{	46	,	0.7193398003	,	0.0061226261	},
{	47	,	0.7313537016	,	0.0060129576	},
{	48	,	0.7431448255	,	0.0059014575	},
{	49	,	0.7547095802	,	0.0057881598	},
{	50	,	0.7660444431	,	0.0056730989	},
{	51	,	0.7771459615	,	0.0055563099	},
{	52	,	0.7880107536	,	0.0054378285	},
{	53	,	0.7986355100	,	0.0053176906	},
{	54	,	0.8090169944	,	0.0051959329	},
{	55	,	0.8191520443	,	0.0050725925	},
{	56	,	0.8290375726	,	0.0049477069	},
{	57	,	0.8386705679	,	0.0048213142	},
{	58	,	0.8480480962	,	0.0046934529	},
{	59	,	0.8571673007	,	0.0045641619	},
{	60	,	0.8660254038	,	0.0044334806	},
{	61	,	0.8746197071	,	0.0043014488	},
{	62	,	0.8829475929	,	0.0041681068	},
{	63	,	0.8910065242	,	0.0040334951	},
{	64	,	0.8987940463	,	0.0038976548	},
{	65	,	0.9063077870	,	0.0037606272	},
{	66	,	0.9135454576	,	0.0036224541	},
{	67	,	0.9205048535	,	0.0034831776	},
{	68	,	0.9271838546	,	0.0033428401	},
{	69	,	0.9335804265	,	0.0032014843	},
{	70	,	0.9396926208	,	0.0030591532	},
{	71	,	0.9455185756	,	0.0029158904	},
{	72	,	0.9510565163	,	0.0027717393	},
{	73	,	0.9563047560	,	0.0026267440	},
{	74	,	0.9612616959	,	0.0024809485	},
{	75	,	0.9659258263	,	0.0023343972	},
{	76	,	0.9702957263	,	0.0021871349	},
{	77	,	0.9743700648	,	0.0020392064	},
{	78	,	0.9781476007	,	0.0018906567	},
{	79	,	0.9816271834	,	0.0017415311	},
{	80	,	0.9848077530	,	0.0015918751	},
{	81	,	0.9876883406	,	0.0014417341	},
{	82	,	0.9902680687	,	0.0012911539	},
{	83	,	0.9925461516	,	0.0011401805	},
{	84	,	0.9945218954	,	0.0009888597	},
{	85	,	0.9961946981	,	0.0008372378	},
{	86	,	0.9975640503	,	0.0006853608	},
{	87	,	0.9986295348	,	0.0005332750	},
{	88	,	0.9993908270	,	0.0003810268	},
{	89	,	0.9998476952	,	0.0002286625	},
{	90	,	1.0000000000	,	0.0000762286	}
}; //}}}

#define nr_of_months (sizeof(months)/sizeof(struct mi))
#define nr_of_entries (sizeof(angles)/sizeof(angles_t))

static int compmi(const void *m1, const void *m2)
{ // {{{
     struct mi *mi1 = (struct mi *) m1;
     struct mi *mi2 = (struct mi *) m2;
     return strcmp(mi1->name, mi2->name);
} // }}}

static int cmpsin( const void *key, const void *data)
{
	angles_t *k = (angles_t *) key;
	angles_t *d = (angles_t *) data;
	if(((k->sine-d->step) < d->sine) && ((k->sine+d->step) > d->sine ))
		return(0);
	if((k->sine+d->step) < d->sine )
		return(-1);
	if((k->sine-d->step) > d->sine )
		return(1);
	return 1; // Error, no match found.
}

int main(int argc, char **argv)
{
	int mode;
	int i,temp;
	int no_of_cycles;
	angles_t  key, *res;

	key.step = (double) 0;
	i = 0;
	mode = 0;

	if( argc > 1 ) {
		if( strcmp( argv[1], "perf" ) == 0 ) {
			no_of_cycles = (int) atoi( (const char*) argv[2] );
			printf("Performance mode for: %d cycles\n", no_of_cycles);
			mode = MODE_PERFORMANCE;
		} 
		if( strcmp( argv[1], "prec" ) == 0 ) {
			no_of_cycles = (int) atoi( (const char*) argv[2] );
			printf("Precision mode for: %d cycles\n", no_of_cycles);
			mode = MODE_PRECISION;
		}
		if( strcmp( argv[1], "base" ) == 0 ) {
			no_of_cycles = (int) atoi( (const char*) argv[2] );
			printf("Basing mode for: %d cycles\n", no_of_cycles);
			mode = MODE_BASE;
		} 
	}

	switch( mode ) {
		case MODE_BASE:
			while( no_of_cycles-- )
			{
				key.sine = (double) rand() / RAND_MAX;
				temp =  asin(key.sine)*180/M_PI;
			}
			printf("Basing for sin(x) done for %s cycles\n", argv[2] );
			break;

		case MODE_PERFORMANCE:
			while( no_of_cycles-- )
			{
				key.sine = (double) rand() / RAND_MAX;
				res = bsearch(&key, angles, nr_of_entries, sizeof(angles_t), cmpsin );
			  	if( res == NULL ) {
					printf("'%f': is not in the look up table. Sorry\n", key.sine);
				} else {
					i++;
		//			printf("Closest to input %s is sine %f for angle = %d\n", argv[1], res->sine, res->angle );
				}
			}
			printf("Requested calculations = %s -- made look-ups = %d\n", argv[1], i );

			break;

		case MODE_PRECISION:
			printf("Input |Match |Angle |Exact\n" );
			printf("--------------------------\n" );
			while( no_of_cycles-- )
			{
				key.sine = (double) rand() / RAND_MAX;
				res = bsearch(&key, angles, nr_of_entries, sizeof(angles_t), cmpsin );
			  	if( res == NULL ) {
					printf("'%f': is not in the look up table. Sorry\n", key.sine);
				} else {
					printf("%6.4f|%6.4f|%2d.000|%6.3f\n", key.sine, res->sine, res->angle, (asin(key.sine)*180/M_PI) );
				}
			}

			break;

		default:
			printf("No mode selected.\n");
			printf("Usage: months <perf|prec|base> <count>\n\n");
			printf("perf - performance mode. It uses bsearch to look up a coarse sin value. The\n");
			printf("       resolution is set as 1 degree (91 tabulated entries for bsearch).\n");
			printf("       The performance is poor, probably due to the complicated comparision\n");
			printf("       method. I try to move it to fixed point aritmetic. Subject to improve\n");
			printf("base - does the classic sin(x) function. It is used to set the base time\n");
			printf("       required by standard implementation of sin(x), using the FPA unit.\n");
			printf("prec - generates table with sin(x) generated by the look-up routine and\n");
			printf("       compared to sin(x) generated by libm.a - stanfard mathematic library\n\n");
			printf("All argument requires the argument <count>. This argument control how many\n");
			printf("cycles will be repeated for the choosed method.\n");


			break;
	}
	
	exit(1);

	res = bsearch(&key, angles, nr_of_entries, sizeof(angles_t), cmpsin );
  	if( res == NULL )
		printf("'%s': is not in the look up table. Sorry\n", argv[1]);
	else
		printf("Closest to input %s is sine %f for angle = %d\n", argv[1], res->sine, res->angle );

/*
	qsort(months, nr_of_months, sizeof(struct mi), compmi);
	for (i = 1; i < argc; i++) {
		struct mi key, *res;
		key.name = argv[i];
		res = bsearch(&key, months, nr_of_months, sizeof(struct mi), compmi);
		if (res == NULL)
			printf("'%s': unknown month\n", argv[i]);
		else
			printf("%s: month #%d\n", res->name, res->nr);
	}
*/

	return 1;
}

